﻿//#define  UIDEBUG

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using OfficeOpenXml;
using System.Collections.ObjectModel;
using System.IO;
using TcpService;

namespace EC3_6CLH.Model
{
    public partial class WorkstationModel : ObservableObject
    {
        public WorkstationModel(int id, string ip, int port, TimeSpan time)
        {
            Id = id;
            Ip = ip;
            Port = port;
            GoalTime = time;
            AxisCurrents = new ObservableCollection<float>();
            for (int i = 0; i < MaxAxisCount; i++)
            {
                AxisCurrents.Add(0);
            }
        }

        [ObservableProperty] private int _id;
        [ObservableProperty] private string _ip;
        [ObservableProperty] private int _port;
        [ObservableProperty] private WorkstationState _workstationState;
        [ObservableProperty] private TimeSpan _runTime;
        [ObservableProperty] private string _sn = "";
        [ObservableProperty] private string _displaySn1 = "";
        [ObservableProperty] private string _displaySn2 = "";
        [ObservableProperty] private bool _isLock = false;  //指示当前工位是否解锁
        [ObservableProperty] private DateTime _startTime;
        public Action<WorkstationState>? WorkstationStateChangedEvent;
        public Action<string>? LogMessageEvent;
        public Action<int, bool> PowerSetEvent;
        private EC3TcpClient EC3TcpClient;
        [ObservableProperty] private float _tempture; //温度

        [ObservableProperty] private ObservableCollection<float> _axisCurrents; //轴电流
        private List<string> _axisErrmsg = new List<string>(); //轴报警信息

        private static Dictionary<UInt16, string> DictErrMessage;
        private List<ReportDetail> _reportDetails = new List<ReportDetail>();
        private string _stdDI = "";
        private string _stdDO = "";
        private string _extDI = "";
        private string _extDO = "";

        private bool _isPnp;

        [ObservableProperty] private bool _axis56Visible;
        [ObservableProperty] private bool _axis78Visible;

        /// <summary>
        /// ping 指令超时时间
        /// </summary>
        public static int PingSeconds { get; set; } = 200;

        /// <summary>
        /// 轴数量
        /// </summary>
        public static int AxisCount { get; set; } = 6;

        /// <summary>
        /// 需要监控拓展IO的工位编号
        /// </summary>

        public static int StationNeedSeeExtIO = 1;
        public const int MaxAxisCount = 8;

        /// <summary>
        /// sn 长度
        /// </summary>
        public const int SnLength = 32;

        private static HashSet<string> _axis4SnHead = new HashSet<string> { "A0", "C0" };
        private static HashSet<string> _axis6SnHead = new HashSet<string> { "A3", "A4", "A5", "A6", "B0", "C3", "C4", "C5", "C6" };
        private static HashSet<string> _axis8SnHead = new HashSet<string> { };
        public static ushort[] ExpectedStdDIPnp;
        public static ushort[] ExpectedStdDINpn;
        public static ushort[] ExpectedStdDO;
        public static ushort[] ExpectedExtDI;
        public static ushort[] ExpectedExtDO;

        static WorkstationModel()
        {
            DictErrMessage = new Dictionary<ushort, string>();
            DictErrMessage.Add(0x0000, "0x0000正常运行");
            DictErrMessage.Add(0x2250, "0x2250驱动器短路");
            DictErrMessage.Add(0x2310, "0x2310U相输出电流过大");
            DictErrMessage.Add(0x2311, "0x2311最大电流限制故障");
            DictErrMessage.Add(0x2312, "0x2312W相电流输出过大");
            DictErrMessage.Add(0x3210, "0x3210直流母线过压");
            DictErrMessage.Add(0x3220, "0x3220直流母线欠压");
            DictErrMessage.Add(0x4000, "0x4000控制板过温故障");
            DictErrMessage.Add(0x4210, "0x4210功率模块过热");
            DictErrMessage.Add(0x5410, "0x5410扭矩关闭故障");
            DictErrMessage.Add(0x6010, "0x6010CPU1看门狗溢出");
            DictErrMessage.Add(0x6100, "0x6100控制环路选择故障");
            DictErrMessage.Add(0x6200, "0x6200ECAT中断时间超时故障");
            DictErrMessage.Add(0x6310, "0x6310读取参数错误");
            DictErrMessage.Add(0x6320, "0x6320电机速度参数矛盾");
            DictErrMessage.Add(0x7110, "0x7110强制抱闸错误");
            DictErrMessage.Add(0x7111, "0x7111抱闸反馈错误");
            DictErrMessage.Add(0x7112, "0x7112能耗制动电阻过载");
            DictErrMessage.Add(0x7320, "0x7320编码器配置故障");
            DictErrMessage.Add(0x7600, "0x7600存储参数错误");
            DictErrMessage.Add(0x8311, "0x8311电机持续过载");
            DictErrMessage.Add(0x8611, "0x8611位置跟随误差过大");
            DictErrMessage.Add(0x8800, "0x8800第一编码器数据溢出");
            DictErrMessage.Add(0xFF05, "0xFF05磁极定位错误");
            DictErrMessage.Add(0xFF06, "0xFF06编码器数据异常");
            DictErrMessage.Add(0xFF07, "0xFF07编码器通信异常");
            DictErrMessage.Add(0xFF08, "0xFF08编码器通信超时");
            DictErrMessage.Add(0xFF09, "0xFF09编码器内部异常1");
            DictErrMessage.Add(0xFF10, "0xFF10驱动器其它轴异常");
            DictErrMessage.Add(0xFF11, "0xFF11电机抱闸断线");
            DictErrMessage.Add(0xFF15, "0xFF15驱动器持续过载");
            DictErrMessage.Add(0xFF17, "0xFF17驱动器输出缺相");
            DictErrMessage.Add(0xFF26, "0xFF26控制电源欠压");
            DictErrMessage.Add(0xFF27, "0xFF27STO1触发");
            DictErrMessage.Add(0xFF28, "0xFF28STO2触发");
            DictErrMessage.Add(0xFF31, "0xFF31电机超速");
            DictErrMessage.Add(0xFF34, "0xFF34速度跟随误差过大");
            DictErrMessage.Add(0xFF37, "0XFF37EtherCAT过程数据错误");
            DictErrMessage.Add(0xFF48, "0xFF48动态制动过载");
            DictErrMessage.Add(0xFF53, "0xFF53编码器过热");
            DictErrMessage.Add(0xFF54, "0xFF54编码器电池欠电压故障");
            DictErrMessage.Add(0xFF57, "0xFF57控制模式设定错误");
            DictErrMessage.Add(0xFF60, "0xFF60电机堵转");
            DictErrMessage.Add(0xFF65, "0xFF65制动模块故障");
            DictErrMessage.Add(0xFF80, "0xFF80主电源掉电");
            DictErrMessage.Add(0xFF97, "0xFF97转矩波动超限");
            DictErrMessage.Add(0xFFB0, "0xFFB0PLL错误");
            DictErrMessage.Add(0xFFB1, "0xFFB1RxPDO丢失");
            DictErrMessage.Add(0xFFB2, "0xFFB2Ecat-Port0端口丢失");
            DictErrMessage.Add(0xFFB3, "0xFFB3Ecat-Port1端口丢失");
            DictErrMessage.Add(0xFFB4, "0xFFB4驱动拒绝通信周期值");
            DictErrMessage.Add(0xFFB5, "0xFFB5Ecat-Port0端口无链接");
            DictErrMessage.Add(0xFFB6, "0xFFB6PDO映射错误");
            DictErrMessage.Add(0xFFB7, "0xFFB7PDO分配错误");
            DictErrMessage.Add(0xFFB8, "0xFFB8同步事件超差");
            DictErrMessage.Add(0xFFB9, "0xFFB9同步事件丢失");
            DictErrMessage.Add(0xFFBA, "0xFFBACSP/CSV/CST模式请求错误");
            DictErrMessage.Add(0xFFBB, "0xFFBBPP模式请求错误");
            DictErrMessage.Add(0xFFBC, "0xFFBCPV模式请求错误");
            DictErrMessage.Add(0xFFC0, "0xFFC0制动电阻泄放异常");
            DictErrMessage.Add(0xFFC2, "0XFFC2数字IO错误");
            DictErrMessage.Add(0xFFC3, "0XFFC3硬件型号错误");
            DictErrMessage.Add(0xFFC4, "0XFFC4自整定故障");
            DictErrMessage.Add(0xFFC5, "0xFFC5风扇模块异常");
            DictErrMessage.Add(0xFFC6, "0xFFC6电机I2T故障");
            DictErrMessage.Add(0xFFC7, "0xFFC7DC电压预充故障");
            DictErrMessage.Add(0xFFC8, "0xFFC8载波周期配置故障");
            DictErrMessage.Add(0xFFC9, "0xFFC9自举失败故障");
            DictErrMessage.Add(0xFFCA, "0xFFCA转矩限制故障");
            DictErrMessage.Add(0xFFD0, "0xFFD0电机位置传感器错误.第一次上电初始化");
            DictErrMessage.Add(0xFFD1, "0xFFD1电机位置传感器请求上电.未初始化");
            DictErrMessage.Add(0xFFD2, "0xFFD2编码器参数位数报警");
            DictErrMessage.Add(0xFFD3, "0xFFD3编码器多圈计数错误");
            DictErrMessage.Add(0xFFD7, "0xFFD7三相阈值未更新故障");
            DictErrMessage.Add(0xFFCB, "0xFFCB零偏电流过大");
            DictErrMessage.Add(0x2716, "0x2716编码器初始化失败");
            DictErrMessage.Add(0xFF22, "0xFF22位置目标值异常");
        }

        private string GetErrMessage(UInt16 key)
        {
            if (DictErrMessage.ContainsKey(key))
            {
                return DictErrMessage[key];
            }
            else
            {
                return $"错误代码[0x{key.ToString("X4")}]未找到错误信息";
            }
        }

        partial void OnWorkstationStateChanged(WorkstationState value)
        {
            WorkstationStateChangedEvent?.Invoke(value);
        }

        partial void OnSnChanged(string value)
        {
            if (value.Length > 10)
            {
                DisplaySn1 = Sn.Substring(0, 10);
                DisplaySn2 = Sn.Substring(10);
            }
            else
            {
                DisplaySn1 = Sn;
                DisplaySn2 = "";
            }
        }

        /// <summary>
        /// 运行的目标时长
        /// </summary>
        public TimeSpan GoalTime { get; init; }

        private async Task DelayAsync(int millSeconds, CancellationToken ct = default(CancellationToken))
        {
            try
            {
                await Task.Delay(millSeconds, ct);
            }
            catch (OperationCanceledException) // 精准捕获取消异常
            {
                throw new OperationCanceledException($"[工位{Id}]操作被取消", ct); // 传递原始Token
            }
        }

        /// <summary>
        /// 开始计时
        /// </summary>
        private async Task StartTiming()
        {
            StartTime = DateTime.Now;
            WorkstationState = WorkstationState.Running;

            while (WorkstationState == WorkstationState.Running)
            {
                RunTime = DateTime.Now - StartTime;
                if (RunTime >= GoalTime)
                {
                    return;
                }
                await DelayAsync(1000);
            }
        }

        private bool CheckSn(string sn)
        {
            if (sn.Length != SnLength)
            {
                return false;
                //throw new Exception($"[工位{Id}]驱动器Sn长度异常");
            }
            if (Sn.Contains("\0"))
            {
                return false;
            }
            string verify = sn.Substring(8, 2);
            if (AxisCount == 4)
            {
                return _axis4SnHead.Contains(verify);
            }
            else if (AxisCount == 6)
            {
                return _axis6SnHead.Contains(verify);
            }
            else if (AxisCount == 8)
            {
                return _axis8SnHead.Contains(verify);
            }
            else
            {
                throw new Exception($"轴数量定义错误[{AxisCount}]");
            }
        }

        private void ResetParam()
        {
            Sn = "";
            for (int i = 0; i < AxisCount; i++)
            {
                AxisCurrents[i] = 0;
            }
            _reportDetails.Clear();

            RunTime = TimeSpan.Zero;
            Tempture = 0;
        }

        private async Task Initialize(CancellationToken ct = default(CancellationToken))
        {
            //供电
            PowerSetEvent.Invoke(Id, true);
            ResetParam();
            LogMessageEvent?.Invoke($"[工位{Id}]初始化开始");

            WorkstationState = WorkstationState.Initializing;
            EC3TcpClient = new EC3TcpClient(Ip);
            var pingResult = await PingChecker.ContinuousPing(Ip, TimeSpan.FromSeconds(PingSeconds), ct);

            if (!pingResult)
            {
                throw new Exception($"[工位{Id}]驱动器{Ip}ping 通信异常");
            }

            await DelayAsync(20000, ct);
            //连接产品
            await EC3TcpClient.ConnectAsync(ct);

            await EC3TcpClient.OpenRCB(ct);

            //read sn
            for (int i = 0; i < 3; i++)
            {
                Sn = await EC3TcpClient.ReadSn(ct: ct);
                if (CheckSn(Sn))
                {
                    break;
                }
                else
                {
                    if (i == 2)
                    {
                        throw new Exception($"[工位{Id}]SN不符合规则[{Sn}]");
                    }
                }
            }
            _isPnp = Sn[11] == 'P';

            foreach (var item in GlobalValue.TestConfig.InitParams)
            {
                LogMessageEvent?.Invoke($"[工位{Id}]写入[{item.Name}, value={item.Data}]");
                await EC3TcpClient.Write(InitParam.Axis, item.Index, item.SubIndex, item.TypeValue, item.DataLen, item.DataValue, ct);
                var data = await EC3TcpClient.Read(InitParam.Axis, item.Index, item.SubIndex, item.TypeValue, item.DataLen, ct);
                if (!item.AreEqual(data))
                {
                    throw new Exception($"[工位{Id}]写入[{item.DataValue}],回读[{data}]");
                }
            }

            // 打开标准DO
            await EC3TcpClient.SetStdDo(true, ct);

            // 打开拓展DO
            await EC3TcpClient.SetExtDo(true, ct);

            LogMessageEvent?.Invoke($"[工位{Id}]初始化完成");
        }

        private async Task Run(CancellationToken ct = default(CancellationToken))
        {
            DebugSth.ResetStep();
            DebugSth.LogStep("start");

            //异步启动计时器
            _ = StartTiming();
            LogMessageEvent?.Invoke($"[工位{Id}]老化开始");

            bool run2Err = false;

            //运行前清除控制器报警
            await EC3TcpClient.ClearAlarm(ct);

            while (WorkstationState == WorkstationState.Running)
            {
                //监控变量
                // 读温度
                Tempture = (float)await EC3TcpClient.Read(1, 0x2101, 0x0C, InitParam.GetDataType("F32"), InitParam.GetDataLen("F32"), ct);

                for (int i = 0; i < AxisCount; i++)
                {
                    var current = (float)((float)await EC3TcpClient.Read(1, (ushort)(0x2103 + i * 0x0800), 0x05, InitParam.GetDataType("F32"), InitParam.GetDataLen("F32"), ct) / 1.5 / 1.414);
                    AxisCurrents[i] = current;
                }
                DebugSth.LogStep("temp");

                List<string> errMsg = new List<string>();

                // 读报警
                _axisErrmsg.Clear();
                for (int i = 0; i < AxisCount; i++)
                {
                    var errcode = (UInt16)await EC3TcpClient.Read(1, (UInt16)(0x603F + i * 0x0800), 0x00, InitParam.GetDataType("U16"), InitParam.GetDataLen("U16"), ct);
                    var errMessage = GetErrMessage(errcode);
                    _axisErrmsg.Add(errMessage);
                    if (errcode != 0)
                    {
                        run2Err = true;
                        LogMessageEvent?.Invoke($"[工位{Id}]{i + 1}轴{errMessage}");
                    }
                }
                DebugSth.LogStep("alarm");

                // 标准IO监控
                _stdDI = "";
                bool stdDiErr = false;
                for (int i = 0; i < 16; i++)
                {
                    var di = (UInt16)await EC3TcpClient.Read(18, 0x6001, (ushort)(0x01 + i), InitParam.GetDataType("U16"), InitParam.GetDataLen("U16"), ct);
                    _stdDI += $"{di} ";

                    ushort expectedValue = ExpectedStdDINpn[i];
                    if (_isPnp)
                    {
                        expectedValue = ExpectedStdDIPnp[i];
                    }
                    if (di != expectedValue)
                    {
                        stdDiErr = true;
                        run2Err = true;
                    }

                    //LogMessageEvent?.Invoke($"工位{Id} DI{i+1} = {di}");
                }
                if (stdDiErr)
                    LogMessageEvent?.Invoke($"[工位{Id}]报警，标准DI[{_stdDI}]");

                DebugSth.LogStep("std di");

                _stdDO = "";
                bool stdDoErr = false;
                for (int i = 0; i < 16; i++)
                {
                    var @do = (UInt16)await EC3TcpClient.Read(18, 0x7001, (ushort)(0x01 + i), InitParam.GetDataType("U16"), InitParam.GetDataLen("U16"), ct);
                    //LogMessageEvent?.Invoke($"工位{Id} DO{i+1} = {di}");
                    if (@do != ExpectedStdDO[i])
                    {
                        run2Err = true;
                        stdDoErr = true;
                    }
                    _stdDO += $"{@do} ";
                }
                if (stdDoErr)
                    LogMessageEvent?.Invoke($"[工位{Id}]报警，标准DO[{_stdDO}]");

                DebugSth.LogStep("std do");

                #region 讀取控制器報警

                var errCount = await EC3TcpClient.ReadAlarmCount(ct);
                string controlErrcodes = "";

                for (short i = 0; i < errCount; i++)
                {
                    var errCode = await EC3TcpClient.ReadAlaramCode((short)(i + 1), ct);
                    controlErrcodes += errCode + ",";
                }

                #endregion 讀取控制器報警

                #region 接外设的工位额外测试项

                if (StationNeedSeeExtIO == Id)
                {
                    // 拓展IO监控
                    _extDI = "";
                    bool extDiErr = false;
                    for (int j = 0; j < 4; j++)
                    {
                        for (int i = 0; i < 16; i++)
                        {
                            var di = (UInt16)await EC3TcpClient.Read(18, (ushort)(0x6010 + j), (ushort)(0x01 + i), InitParam.GetDataType("U16"), InitParam.GetDataLen("U16"), ct);
                            _extDI += $"{di} ";
                            if (di != ExpectedExtDI[j * 16 + i])
                            {
                                run2Err = true;
                                extDiErr = true;
                            }
                        }
                    }
                    DebugSth.LogStep("ext di");

                    if (extDiErr)
                        LogMessageEvent?.Invoke($"[工位{Id}]扩展DI[{_extDI}]");

                    _extDO = "";
                    bool extDoErr = false;
                    for (int j = 0; j < 4; j++)
                    {
                        for (int i = 0; i < 16; i++)
                        {
                            var @do = (UInt16)await EC3TcpClient.Read(18, (ushort)(0x7010 + j), (ushort)(0x01 + i), InitParam.GetDataType("U16"), InitParam.GetDataLen("U16"), ct);
                            _extDO += $"{@do} ";
                            if (@do != ExpectedExtDO[j * 16 + i])
                            {
                                run2Err = true;
                                extDoErr = true;
                            }
                        }
                    }
                    DebugSth.LogStep("ext do");

                    if (extDoErr)
                        LogMessageEvent?.Invoke($"[工位{Id}]扩展DO[{_extDO}]");
                }

                #endregion 接外设的工位额外测试项

                #region 记录报告

                ReportDetail reportDetail = new ReportDetail();
                reportDetail.Time = DateTime.Now.ToString("yyyyMMdd HH:mm:ss");
                reportDetail.Temperature = Tempture;
                for (int i = 0; i < AxisCount; i++)
                {
                    reportDetail.AxisCurrents.Add(AxisCurrents[i]);
                    reportDetail.AxisErrmsg.Add(_axisErrmsg[i]);
                }
                reportDetail.StdDi = _stdDI;
                reportDetail.StdDo = _stdDO;

                reportDetail.ExtDi = _extDI;
                reportDetail.ExtDo = _extDO;
                reportDetail.ControlErrcodes = controlErrcodes;
                _reportDetails.Add(reportDetail);

                #endregion 记录报告

                if (run2Err)
                {
                    WorkstationState = WorkstationState.Ng;
                }

                if (RunTime < GoalTime)
                {
#if UIDEBUG
                    if (RunTime > TimeSpan.FromSeconds(3))
                    {
                        if (id == 4)
                        {
                            WorkstationState = WorkstationState.Ng;
                            LogMessageEvent?.Invoke($"工位{id}老化失败!");
                        }
                    }
#endif
                    if (!CheckDevStatus())
                    {
                        WorkstationState = WorkstationState.Ng;
                        LogMessageEvent?.Invoke($"[工位{Id}]老化失败!");
                    }
                }
                else
                {
                    LogMessageEvent?.Invoke($"[工位{Id}]老化成功");
                    WorkstationState = WorkstationState.Ok;
                }
                await DelayAsync(10, ct);

#if UIDEBUG

                if (RunTime > TimeSpan.FromSeconds(3))
                {
                    if (id == 3)
                    {
                        throw new Exception("hello error");
                    }
                }
#endif
            }

            // 记录测试报告
        }

        //校验驱动器状态
        private bool CheckDevStatus()
        {
            return true;
        }

        private CancellationTokenSource _stopCancellationTokenSource = new CancellationTokenSource();

        #region Public

        public void Reset()
        {
            if (IsLock)
            {
                return;
            }
            if (WorkstationState == WorkstationState.Initializing || WorkstationState == WorkstationState.Running)
            {
                return;
            }
            WorkstationState = WorkstationState.Idle;
            RunTime = TimeSpan.Zero;
            ResetParam();
        }

        [RelayCommand]
        public async Task StartTestAsync()
        {
            await Task.Run(async () =>
            {
                if (IsLock)
                {
                    return;
                }
                if (WorkstationState == WorkstationState.Initializing || WorkstationState == WorkstationState.Running
                   || WorkstationState == WorkstationState.Error)
                {
                    return;
                }
                _stopCancellationTokenSource = new CancellationTokenSource();
                await StartTest(_stopCancellationTokenSource.Token);
                //await GenerateReportAsync();
                if (WorkstationState == WorkstationState.Ok ||WorkstationState == WorkstationState.Ng)
                {
                    await GenerateMesReportAsync();
                }
            });
        }

        [RelayCommand]
        public async Task StopAsync()
        {
            _stopCancellationTokenSource.Cancel();
            await Task.Delay(10);
        }

        #endregion Public

        #region 测试报告

        public static string BaseReportPath { get; set; } = "";

        /// <summary>
        /// 生成测试报告
        /// </summary>
        /// <returns></returns>
        private async Task GenerateReportAsync()
        {
            try
            {
                await Task.Delay(10);
                if (_reportDetails.Count == 0)
                {
                    return;
                }
                // 确保目录存在
                if (!Directory.Exists(BaseReportPath))
                {
                    Directory.CreateDirectory(BaseReportPath);
                }
                // 生成文件名：EC3A_当前日期当前时间.xlsx
                string fileName = $"{Sn.Replace("*", "-")}_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
                string fullPath = Path.Combine(BaseReportPath, fileName);

                // 设置EPPlus许可证上下文（必须）
                ExcelPackage.License.SetNonCommercialOrganization("BoYao");
                using (var package = new ExcelPackage())
                {
                    // 创建Detail工作表
                    var worksheet = package.Workbook.Worksheets.Add("Detail");

                    // 生成表头
                    GenerateHeaders(worksheet, _reportDetails);

                    // 填充数据
                    FillData(worksheet, _reportDetails);

                    // 自动调整列宽
                    worksheet.Cells.AutoFitColumns();

                    // 保存文件
                    package.SaveAs(new FileInfo(fullPath));
                }
            }
            catch (Exception ex)
            {
                LogMessageEvent?.Invoke($"[工位{Id}]输出测试报告异常{ex.Message}");
            }
        }

        /// <summary>
        /// 生成Mes报告
        /// </summary>
        /// <returns></returns>
        private async Task GenerateMesReportAsync()
        {
            try
            {
                await Task.Delay(10);
                if (_reportDetails.Count == 0)
                {
                    return;
                }
                // 确保目录存在
                if (!Directory.Exists(BaseReportPath))
                {
                    Directory.CreateDirectory(BaseReportPath);
                }
                // 生成文件名：EC3A_当前日期当前时间.xlsx
                string staus = "PASS";
                switch (WorkstationState)
                {
                    case WorkstationState.Idle:
                        return;

                    case WorkstationState.Initializing:
                        return;

                    case WorkstationState.Running:
                        return;

                    case WorkstationState.Error:
                        staus="FAIL";
                        break;

                    case WorkstationState.Stop:
                        return;

                    case WorkstationState.Ok:
                        break;

                    case WorkstationState.Ng:
                        staus="FAIL";
                        break;

                    default:
                        break;
                }
                string fileName = $"{Sn.Replace("*", "-")}_{DateTime.Now:yyyyMMddHHmmss}_{staus}.xlsx";
                string fullPath = Path.Combine(BaseReportPath, fileName);

                // 设置EPPlus许可证上下文（必须）
                ExcelPackage.License.SetNonCommercialOrganization("BoYao");
                using (var package = new ExcelPackage())
                {
                    // 创建Detail工作表
                    var worksheet = package.Workbook.Worksheets.Add("Detail");

                    // 生成表头
                    GenerateMesHeaders(worksheet, _reportDetails);

                    // 填充数据
                    FillMesData(worksheet, _reportDetails, staus);

                    // 自动调整列宽
                    worksheet.Cells.AutoFitColumns();

                    // 保存文件
                    package.SaveAs(new FileInfo(fullPath));
                }
            }
            catch (Exception ex)
            {
                LogMessageEvent?.Invoke($"[工位{Id}]输出测试报告异常{ex.Message}");
            }
        }

        // <summary>
        /// 生成表头
        /// </summary>
        private static void GenerateHeaders(ExcelWorksheet worksheet, List<ReportDetail> reportData)
        {
            int col = 1;

            // 固定表头
            worksheet.Cells[1, col++].Value = "时间戳";
            worksheet.Cells[1, col++].Value = "驱动器温度";

            // 动态轴电流表头（根据第一个数据项的AxisCurrents数量）
            if (reportData[0].AxisCurrents.Count > 0)
            {
                for (int i = 0; i < reportData[0].AxisCurrents.Count; i++)
                {
                    worksheet.Cells[1, col++].Value = $"{i + 1}轴电流";
                }
            }

            // 动态轴错误信息表头（根据第一个数据项的AxisErrmsg数量）
            if (reportData.Count > 0 && reportData[0].AxisErrmsg.Count > 0)
            {
                for (int i = 0; i < reportData[0].AxisErrmsg.Count; i++)
                {
                    worksheet.Cells[1, col++].Value = $"{i + 1}轴报警信息";
                }
            }

            // 固定表尾
            worksheet.Cells[1, col++].Value = "标准DI";
            worksheet.Cells[1, col++].Value = "标准DO";
            worksheet.Cells[1, col++].Value = "拓展DI";
            worksheet.Cells[1, col++].Value = "拓展DO";
            worksheet.Cells[1, col++].Value = "控制器报警代码";

            // 设置表头样式
            using (var range = worksheet.Cells[1, 1, 1, col - 1])
            {
                range.Style.Font.Bold = true;
                range.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
                range.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray);
            }
        }

        // <summary>
        /// 生成Mes表头
        /// </summary>
        private void GenerateMesHeaders(ExcelWorksheet worksheet, List<ReportDetail> reportData)
        {
            int col = 1;

            // 固定表头
            worksheet.Cells[1, col++].Value = "SN";
            worksheet.Cells[1, col++].Value = "结果(Pass or Fail)";
            worksheet.Cells[1, col++].Value = "工位编号";
            worksheet.Cells[1, col++].Value = "IP";
            worksheet.Cells[1, col++].Value = "时间戳";
            worksheet.Cells[1, col++].Value = "驱动器温度";

            // 动态轴电流表头（根据第一个数据项的AxisCurrents数量）
            if (reportData[0].AxisCurrents.Count > 0)
            {
                for (int i = 0; i < reportData[0].AxisCurrents.Count; i++)
                {
                    worksheet.Cells[1, col++].Value = $"{i + 1}轴电流";
                }
            }

            // 动态轴错误信息表头（根据第一个数据项的AxisErrmsg数量）
            if (reportData.Count > 0 && reportData[0].AxisErrmsg.Count > 0)
            {
                for (int i = 0; i < reportData[0].AxisErrmsg.Count; i++)
                {
                    worksheet.Cells[1, col++].Value = $"{i + 1}轴报警信息";
                }
            }

            // 固定表尾
            worksheet.Cells[1, col++].Value = "标准DI";
            worksheet.Cells[1, col++].Value = "标准DO";
            worksheet.Cells[1, col++].Value = "拓展DI";
            worksheet.Cells[1, col++].Value = "拓展DO";
            worksheet.Cells[1, col++].Value = "控制器报警代码";

            // 设置表头样式
            using (var range = worksheet.Cells[1, 1, 1, col - 1])
            {
                range.Style.Font.Bold = true;
                range.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
                range.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray);
            }
        }

        /// <summary>
        /// 填充数据到工作表
        /// </summary>
        private static void FillData(ExcelWorksheet worksheet, List<ReportDetail> reportData)
        {
            for (int row = 0; row < reportData.Count; row++)
            {
                var data = reportData[row];
                int col = 1;

                // 时间
                worksheet.Cells[row + 2, col++].Value = data.Time;

                // 温度
                worksheet.Cells[row + 2, col++].Value = data.Temperature;

                // 轴电流数据
                foreach (var current in data.AxisCurrents)
                {
                    worksheet.Cells[row + 2, col++].Value = current;
                }

                // 轴错误信息
                foreach (var errMsg in data.AxisErrmsg)
                {
                    worksheet.Cells[row + 2, col++].Value = errMsg;
                }

                // 标准DI/DO和拓展DI/DO
                worksheet.Cells[row + 2, col++].Value = data.StdDi;
                worksheet.Cells[row + 2, col++].Value = data.StdDo;
                worksheet.Cells[row + 2, col++].Value = data.ExtDi;
                worksheet.Cells[row + 2, col++].Value = data.ExtDo;

                // 控制器报警记录
                worksheet.Cells[row + 2, col++].Value = data.ControlErrcodes;
            }
        }

        /// <summary>
        /// 填充数据到Mes工作表
        /// </summary>
        private void FillMesData(ExcelWorksheet worksheet, List<ReportDetail> reportData, string res)
        {
            int initcol = 1;
            int initrow = 2;

            worksheet.Cells[initrow, initcol++].Value = Sn;
            worksheet.Cells[initrow, initcol++].Value = res;
            worksheet.Cells[initrow, initcol++].Value = Id;
            worksheet.Cells[initrow, initcol++].Value = Ip;

            for (int row = 0; row < reportData.Count; row++)
            {
                var data = reportData[row];
                int col = 1+4;

                // 时间
                worksheet.Cells[row + 2, col++].Value = data.Time;

                // 温度
                worksheet.Cells[row + 2, col++].Value = data.Temperature;

                // 轴电流数据
                foreach (var current in data.AxisCurrents)
                {
                    worksheet.Cells[row + 2, col++].Value = current;
                }

                // 轴错误信息
                foreach (var errMsg in data.AxisErrmsg)
                {
                    worksheet.Cells[row + 2, col++].Value = errMsg;
                }

                // 标准DI/DO和拓展DI/DO
                worksheet.Cells[row + 2, col++].Value = data.StdDi;
                worksheet.Cells[row + 2, col++].Value = data.StdDo;
                worksheet.Cells[row + 2, col++].Value = data.ExtDi;
                worksheet.Cells[row + 2, col++].Value = data.ExtDo;

                // 控制器报警记录
                worksheet.Cells[row + 2, col++].Value = data.ControlErrcodes;
            }
        }

        #endregion 测试报告

        private async Task StartTest(CancellationToken ct = default(CancellationToken))
        {
            try
            {
                await Initialize(ct);
                await Run(ct);
            }
            catch (OperationCanceledException)
            {
                LogMessageEvent?.Invoke($"[工位{Id}]停止");
                WorkstationState = WorkstationState.Stop; // 取消时重置状态
            }
            catch (Exception e)
            {
                LogMessageEvent?.Invoke($"[工位{Id}]发生错误{e.Message}!");
                WorkstationState = WorkstationState.Error;
            }
            finally
            {
                // edit 20251024 测试通过时自动断电
                if (WorkstationState == WorkstationState.Ok)
                {
                    PowerSetEvent.Invoke(Id, false);
                }
            }
        }
    }
}